package mbp.message;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import mbp.base.FeatureType;
import mbp.base.Record;
import mbp.base.ResourceSet;
import mbp.base.ResourceType;
import mbp.base.ServiceStage;
import mbp.core.Contract;
import mbp.core.CounterOffer;
import mbp.core.Intention;
import mbp.core.Offer;
import mbp.core.Resource;
import mbp.core.ResourceTypeBuilder;
import mbp.core.ResourceUtility;
import mbp.core.ResourceValidation;
import mbp.core.Service;
import mbp.core.ServiceTypeBuilder;
import mbp.exceptions.InvalidEnumTypeException;
import mbp.exceptions.InvalidFeatureNameException;
import mbp.exceptions.InvalidFeatureTypeException;
import mbp.exceptions.InvalidFeatureValueException;
import mbp.exceptions.InvalidResourceTypeException;
import mbp.exceptions.InvalidValueException;

import org.apache.log4j.Logger;

/**
 * Implementuje konwersje obiektow systemu do obiektow informacyjnych i odwrotnie
 * @author wojtek
 *
 */
public class ObjectBuilder 
{
	private Logger logger = Logger.getLogger(this.getClass());
	ResourceTypeBuilder rtb;
	ServiceTypeBuilder stb;
	
	public ObjectBuilder()
	{
		rtb = new ResourceTypeBuilder("");
		stb = new ServiceTypeBuilder("");
	}
	
	/**
	 * Zwraca obiekt informacyjny mbp.message.resource odwzorowujacy rzeczywisty obiekt mbp.core.Resource
	 * @param resource - obiekt typu mbp.core.Resource
	 * @return obiekt typu mbp.message.resource
	 */
	public resource getResourceInfo(Resource resource)
	{
		resource resourceXml = new resource();
		resourceXml.type = resource.getType().getResourceTypeName();
		resourceXml.owner = resource.getOwner();
		resourceXml.resourceid = resource.getUUID();
		resourceXml.vdate = resource.getValidDateStr();
		resourceXml.quantity = resource.getQuantity().toString();
		
		Iterator<String> it = resource.getType().getFeaturesCollection().keySet().iterator();
		
		while(it.hasNext())
		{
			String key = it.next();
			//String value = "";
			try {
				FeatureType type = resource.getFeatureType(key);
				Object ob = resource.getFeatureValue(key);
				feature ft;
				switch(type)
				{
				case INT:
					Integer valuei = (Integer)ob;
					//hm.put(key, valuei.toString());
					//feature.setAttribute("value", valuei.toString());
					ft = new feature(key, valuei.toString());
					resourceXml.features.add(ft);
					break;
				case FLOAT:
					Float valuef = (Float)ob;
					//feature.setAttribute("value", valuef.toString());
					ft = new feature(key, valuef.toString());
					resourceXml.features.add(ft);
					break;
				case ENUM:
					//Enum<?> valuee = (Enum<?>)ob;
					//feature.setAttribute("value", ob.toString());
					ft = new feature(key, ob.toString());
					resourceXml.features.add(ft);
					break;
				}
				
				
				
			} catch (InvalidFeatureNameException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureNameException", e);
			} catch (Exception e) {
				logger.error("Exception", e);
			}
			
			
		}
		
		//logger.debug("Created new resource info from Resource object: " + resourceXml.type);
		return resourceXml;
	}
	
	/**
	 * Zwraca obiekt informacyjny mbp.message.utility odwzorowujacy rzeczywisty obiekt mbp.core.ResourceUtility
	 * @param rcu - obiekt typu mbp.core.ResourceUtility
	 * @return obiekt typu mbp.message.utility
	 */
	public utility getUtilityInfo(ResourceUtility rcu)
	{
		String type = rcu.getType().getResourceTypeName();
		String expr = rcu.getExrpession();
		
		//MessageXml.resource resourceXml = contentXml.new resource();
		utility utilityXml = new utility();
		utilityXml.type = type;
		utilityXml.expression = expr;
		
		Iterator<String> it1 = rcu.getTableNames().iterator();
		while(it1.hasNext())
		{
			String key = it1.next();
			
			Iterator<String> it2 = rcu.getTableValues(key).keySet().iterator();
			
			while(it2.hasNext())
			{
				String en = it2.next();
				Integer v = rcu.getTableValues(key).get(en);
				
				enumvalue enumvalueXml = new enumvalue();
				enumvalueXml.feature = key;
				enumvalueXml.svalue = en;
				enumvalueXml.ivalue = v.toString();
				
				utilityXml.enumvalues.add(enumvalueXml);
			}
		}
		//rcUtility.
		
		//contentXml.utilities.add(utilityXml);
		
		//logger.debug("Created new utility info from ResourceUtility object: " + utilityXml.type);
		return utilityXml;
	}
	
	/**
	 * Zwraca obiekt informacyjny mbp.message.validation odwzorowujacy rzeczywisty obiekt mbp.core.ResourceValidation
	 * @param rcv - obiekt typu mbp.core.ResourceValidation
	 * @return obiekt typu mbp.message.validation
	 */
	public validation getValidationInfo(ResourceValidation rcv)
	{
		String type = rcv.getType().getResourceTypeName();
		String cnd = rcv.getCondition();
		String intvalue = rcv.integerValue.toString();
		String qvalue = rcv.getQuantity().toString();
		
		//MessageXml.resource resourceXml = contentXml.new resource();
		validation validationXml =  new validation();
		validationXml.type = type;
		validationXml.condition = cnd;
		validationXml.intvalue = intvalue;
		validationXml.quantity = qvalue;
		
		//logger.debug("Created new validation info from ResourceValidation object: " + validationXml.type);
		return validationXml;
	}
	
	/**
	 * Zwraca obiekt informacyjny mbp.message.service odwzorowujacy rzeczywisty obiekt mbp.core.Service
	 * @param srv - obiekt typu mbp.core.Service
	 * @return obiekt typu mbp.message.service
	 */
	public service getServiceInfo(Service srv)
	{
		offer serviceXml = new offer();
		
		serviceXml.type = srv.getType().getServiceTypeName();
		serviceXml.offerValidity = srv.getOfferValidTerm().toString();
		serviceXml.realizeTerm = srv.getServiceRealizeTerm().toString();
		
		//out
		for(int i = 0; i<srv.getResourcesOut().size(); i++)
		{
			resource resourceXml = getResourceInfo(srv.getResourcesOut().get(i));
			serviceXml.rcOut.add(resourceXml);
		}
		
		//in
		for(int i = 0; i<srv.getResourcesIn(-1).size(); i++)
		{
			validation validationXml = getValidationInfo(srv.getResourcesIn(-1).get(i));
			serviceXml.rcvIn.add(validationXml);			
		}
		
		//realize terms
		for(int i = 0; i<srv.getResourcesInDeliveryTrems().size(); i++)
		{
			serviceXml.resourcesTerm.add(srv.getResourcesInDeliveryTrems().get(i).toString());
		}
		
		//logger.debug("Created new service info from Service object: " + serviceXml.type);
		return serviceXml;
	}
	
	/**
	 * Zwraca obiekt informacyjny mbp.message.intention odwzorowujacy rzeczywisty obiekt mbp.core.Intention
	 * @param intn - obiekt typu mbp.core.Intention
	 * @return
	 */
	public intention getIntentionInfo(Intention intn)
	{
		//intention intentionXml = new intention();
		counteroffer intentionXml = new counteroffer();
		
		intentionXml.type = intn.getServiceType().getServiceTypeName();
		intentionXml.client = intn.getClientName();
		intentionXml.agent = intn.getAgentName();
		intentionXml.maxterm = intn.getRealizeMaxTU().toString();
		
		for(int i = 0; i<intn.getResourcesOut().size(); i++)
		{
			validation va = getValidationInfo(intn.getResourcesOut().get(i));
			intentionXml.rcvOut.add(va);
		}
		
		//logger.debug("Created new intention info from Intention object: " + intentionXml.type);
		return intentionXml;
	}
	
	public offer getOfferInfo(Offer ofr)
	{
		offer offerXml = (offer) getServiceInfo(ofr);
		
		offerXml.uuid = ofr.GetUUID();
		offerXml.cdate = mbp.base.DateString.DateToStr(ofr.getOfferCreateDate());
		offerXml.client = ofr.getClientName();
		offerXml.server = ofr.getServerName();
		offerXml.agent = ofr.getAgentName();
		
		//logger.debug("Created new offer info from Offer object: " + offerXml.type);
		return offerXml;
	}
	
	
	public counteroffer getCouterOfferInfo(CounterOffer cofr)
	{
		counteroffer cofferXml = (counteroffer) getIntentionInfo(cofr);
		
		cofferXml.offeruuid = cofr.GetUUID();
		List<Record<Resource, Object[]>> rcvInAdvance = cofr.getResourcesIn(ServiceStage.ADVANCE);
		List<Record<Resource, Object[]>> rcvInStuff = cofr.getResourcesIn(ServiceStage.STUFF);
		List<Record<Resource, Object[]>> rcvInPay = cofr.getResourcesIn(ServiceStage.PAY);
		
		for(int i = 0; i<rcvInAdvance.size(); i++)
		{
			resourceset rcs = new resourceset();
			rcs.type = (String) rcvInAdvance.get(i).value1[0];
			if(rcvInAdvance.get(i).value0 != null)
			{
				resource rc = getResourceInfo(rcvInAdvance.get(i).value0);
				rcs.rc = rc;
			}			
			cofferXml.rcsadvance.add(rcs);
		}
		
		for(int i = 0; i<rcvInStuff.size(); i++)
		{
			resourceset rcs = new resourceset();
			rcs.type = (String) rcvInStuff.get(i).value1[0];
			rcs.term = rcvInStuff.get(i).value1[1].toString();
			if(rcvInStuff.get(i).value0 != null)
			{
				resource rc = getResourceInfo(rcvInStuff.get(i).value0);
				rcs.rc = rc;
			}			
			cofferXml.rcsstuff.add(rcs);
		}
		
		for(int i = 0; i<rcvInPay.size(); i++)
		{
			resourceset rcs = new resourceset();
			rcs.type = (String) rcvInPay.get(i).value1[0];
			//rcs.term = rcvInPay.get(i).term.toString();
			if(rcvInPay.get(i).value0 != null)
			{
				resource rc = getResourceInfo(rcvInPay.get(i).value0);
				rcs.rc = rc;
			}			
			cofferXml.rcspay.add(rcs);
		}
		
		//logger.debug("Created new counteroffer info from CounterOffer object: " + cofferXml.type);
		return cofferXml;
	}
	
	public contract getContractInfo(Contract contract)
	{
		contract cntri = new contract();
		
		cntri.uuid = contract.GetUUID();
		cntri.type = contract.getOffer().getType().getServiceTypeName();
		cntri.state = contract.getContractState().toString();
		cntri.server = contract.getServerName();
		cntri.client = contract.getClientName();
		cntri.agent = contract.getAgentName();
		//cntri.cservice
		//cntri.signed
		//contract.getOffer().
		
		return cntri;
	}
	
	//-----------------------------------------------------------------------------------------------------------------
	
	/**
	 * Tworzy i zwraca obiekt mbp.core.Resource na podstawie obiektu informacyjnego mbp.message.resource
	 * @param rci - obiekt typu mbp.message.resource
	 * @return
	 */
	public Resource getResource(resource rci)
	{
		Resource resource = null;
		
		ResourceType resourceType = rtb.GetResourceType(rci.type);
		
		resource = new Resource(resourceType, rci.resourceid);
		resource.setOwner(rci.owner);
		try {
			resource.setValidDateStr(rci.vdate);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			logger.error("ParseException", e);
		}
		
		try {
			resource.setQuantity(Integer.parseInt(rci.quantity));
		} catch (NumberFormatException e1) {
			// TODO Auto-generated catch block
			logger.error("NumberFormatException", e1);
		} catch (InvalidValueException e1) {
			// TODO Auto-generated catch block
			logger.error("InvalidValueException", e1);
		}
		
		for(int i=0; i<rci.features.size(); i++)
		{
			//resource.SetFeatureValue(featureName, value)
			String key = rci.features.get(i).getName();
			String value = rci.features.get(i).getValue();
			try {
				FeatureType ftype = resource.getFeatureType(key);
				
				switch(ftype)
				{
				case INT:
					Integer valuei = Integer.parseInt(value);
					resource.setFeatureValue(key, valuei);
					break;
				case FLOAT:
					Float valuef = Float.parseFloat(value);
					resource.setFeatureValue(key, valuef);
					break;
				case ENUM:
					//Class cl = Class.forName(resourceType.GetEnumClassName(key));
					//Enum<?> valuee = Enum.valueOf(cl, value);
					//resource.SetFeatureValue(key, valuee);
					String valuee = value;
					resource.setFeatureValue(key, valuee);
					break;
				}
				
				
			} catch (InvalidFeatureNameException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureNameException", e);
			} catch (InvalidFeatureTypeException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureTypeException", e);
			} catch (InvalidFeatureValueException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureValueException", e);
			} catch (Exception e) {
				logger.error("Exception", e);
			}
		}
		
		//logger.debug("Created new Resource object from resource info: " + resource.GetType().GetResourceTypeName());
		return resource;
	}
	
	public ResourceUtility getResourceUtility(utility rcui)
	{
		String type = rcui.type;
		String expr = rcui.expression;
		ResourceUtility ru = new ResourceUtility(rtb.GetResourceType(type));
		ru.setExpression(expr);
		
		for(int j = 0; j<rcui.enumvalues.size(); j++)
		{
			String key = rcui.enumvalues.get(j).feature;
			String ivalue = rcui.enumvalues.get(j).ivalue;
			String svalue = rcui.enumvalues.get(j).svalue;
			
			try {
				ru.setEnumValue(key, svalue, Integer.parseInt(ivalue));
			} catch (NumberFormatException e) {
				// TODO Auto-generated catch block
				logger.error("NumberFormatException", e);
			} catch (InvalidFeatureNameException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureNameException", e);
			} catch (InvalidEnumTypeException e) {
				// TODO Auto-generated catch block
				logger.error("InvalidEnumTypeException", e);
			}
		}
		
		//logger.debug("Created new ResourceUtility object from utility info: " + ru.GetType().GetResourceTypeName());
		return ru;
	}
	
	public ResourceValidation getResourceValidation(validation rcvi)
	{
		String type = rcvi.type;
		String cnd = rcvi.condition;
		ResourceValidation rv = new ResourceValidation(rtb.GetResourceType(type));
		rv.setCondition(cnd);
		rv.integerValue = Integer.parseInt(rcvi.intvalue);
		rv.setQuantity(Integer.parseInt(rcvi.quantity));
		
		//logger.debug("Created new ResourceValidation object from validation info: " + rv.GetType().GetResourceTypeName());
		return rv;
	}
	
	public Service getService(service srvi)
	{
		//Service service = new Service(stb.GetServiceType(srvi.type));
		Offer service = new Offer(stb.GetServiceType(srvi.type));
		
		service.setOfferValidTerm(Integer.parseInt(srvi.offerValidity));
		//service.setServiceRealizeTerm(Integer.parseInt(srvi.realizeTerm));
		
		//out
		for(int i = 0; i<service.getResourcesOut().size(); i++)
		{
			Resource rc = service.getResourcesOut().get(i);
			
			if(rc.getType().getResourceTypeName().compareTo(srvi.rcOut.get(i).type) == 0)
			{
				rc.setOwner(srvi.rcOut.get(i).owner);
				
				try {
					setResourceFeatures(srvi.rcOut.get(i), rc);
				} catch (InvalidFeatureNameException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureNameException", e);
				} catch (InvalidFeatureTypeException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureTypeException", e);
				} catch (InvalidFeatureValueException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureValueException", e);
				} catch (InvalidResourceTypeException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidResourceTypeException", e);
				}
				
			}
		}
		
		//in
		for(int i = 0; i<service.getResourcesIn(-1).size(); i++)
		{
			ResourceValidation rv = service.getResourcesIn(-1).get(i);
			
			if(rv.getType().getResourceTypeName().compareTo(srvi.rcvIn.get(i).type) == 0)
			{
				String cnd = srvi.rcvIn.get(i).condition;
				String quantity = srvi.rcvIn.get(i).quantity;
				rv.setCondition(cnd);
				rv.setQuantity(Integer.parseInt(quantity));
			}
		}
		
		//realize term
		List<Integer> rcTermTU = new ArrayList<Integer>();
		for(int i = 0; i<srvi.resourcesTerm.size(); i++)
		{
			rcTermTU.add(Integer.parseInt(srvi.resourcesTerm.get(i)));
		}
		service.setResourcesInDeliveryTrems((ArrayList<Integer>) rcTermTU);
		
		service.setServiceRealizeTerm(Integer.parseInt(srvi.realizeTerm));
		
		
		//logger.debug("Created new Service object from service info: " + service.getServiceType().GetServiceTypeName());

		return service;	
	}
	
	public Intention getIntention(intention intni)
	{
		//Intention intn = new Intention(stb.GetServiceType(intni.type));
		CounterOffer intn = new CounterOffer(stb.GetServiceType(intni.type));
		
		intn.setClientName(intni.client);
		intn.setAgentName(intni.agent);
		intn.setRealizeMaxTU(Integer.parseInt(intni.maxterm));
		
		for(int i = 0; i<intn.getResourcesOut().size(); i++)
		{
			if(intn.getResourcesOut().get(i).getType().getResourceTypeName().compareTo(intni.rcvOut.get(i).type) == 0)
			{
				intn.getResourcesOut().get(i).setCondition(intni.rcvOut.get(i).condition);
				intn.getResourcesOut().get(i).setQuantity(Integer.parseInt( intni.rcvOut.get(i).quantity ));
			}
		}
		
		//logger.debug("Created new Intention object from intention info: " + intn.getServiceType().GetServiceTypeName());
		return intn;
	}
	
	public Offer getOffer(offer ofr)
	{
		Offer offer = (Offer) getService(ofr);
		String uuid = ofr.uuid;
		Date createDate = null;
		try {
			createDate = mbp.base.DateString.StrToDate(ofr.cdate);
		} catch (ParseException e) {
			logger.error("ParseException", e);
		}
		offer.initialize(uuid, createDate);
		offer.setClientName(ofr.client);
		offer.setServerName(ofr.server);
		offer.setAgentName(ofr.agent);
		
		//logger.debug("Created new Offer object from offer info: " + offer.getServiceType().GetServiceTypeName());
		return offer;
	}
	
//	public CounterOffer getCounterOffer(counteroffer cofri)
//	{
//		return null;
//	}
	
	public CounterOffer getCounterOffer(counteroffer cofri)
	{
		CounterOffer cofr = (CounterOffer) getIntention(cofri);
		cofr.setUUID(cofri.offeruuid);
		
		List<resourceset> rcsInAdvance = cofri.rcsadvance;
		List<resourceset> rcsInStuff = cofri.rcsstuff;
		List<resourceset> rcsInPay = cofri.rcspay;
		
		for(int i = 0; i<rcsInAdvance.size(); i++)
		{
			String type = rcsInAdvance.get(i).type;
			//Integer term = Integer.parseInt(rcsInAdvance.get(i).term);
			//ResourceSet rs = new ResourceSet(i, type, null);
			Record<Resource, Object[]> rs = new Record<Resource, Object[]>(null, new Object[]{type, new Integer(0)});
			if(rcsInAdvance.get(i).rc != null)
			{
				Resource rc = getResource(rcsInAdvance.get(i).rc);
				//rs.setResouceInfo(rc);
				rs.value0 = rc;
			}
			cofr.getResourcesIn(ServiceStage.ADVANCE).add(rs);
		}
		
		for(int i = 0; i<rcsInStuff.size(); i++)
		{
			String type = rcsInStuff.get(i).type;
			Integer term = Integer.parseInt(rcsInStuff.get(i).term);
			//ResourceSet rs = new ResourceSet(i, type, term);
			Record<Resource, Object[]> rs = new Record<Resource, Object[]>(null, new Object[]{type, term});
			if(rcsInStuff.get(i).rc != null)
			{
				Resource rc = getResource(rcsInStuff.get(i).rc);
				rs.value0 = rc;
			}
			cofr.getResourcesIn(ServiceStage.STUFF).add(rs);
		}
		
		for(int i = 0; i<rcsInPay.size(); i++)
		{
			String type = rcsInPay.get(i).type;
			//Integer term = Integer.parseInt(rcsInPay.get(i).term);
			//ResourceSet rs = new ResourceSet(i, type, null);
			Record<Resource, Object[]> rs = new Record<Resource, Object[]>(null, new Object[]{type, new Integer(0)});
			if(rcsInPay.get(i).rc != null)
			{
				Resource rc = getResource(rcsInPay.get(i).rc);
				rs.value0 = rc;
			}
			cofr.getResourcesIn(ServiceStage.PAY).add(rs);
		}
		
		//logger.debug("Created new CounterOffer object from counteroffer info: " + cofr.getServiceType().GetServiceTypeName());
		return cofr;
	}
	
	
	//-----------------------------------------------------------------------------------------------------------------
	
	/**
	 * Ustawia wartosci cech zasobu na podstawie obiektu MessageXml.resource
	 * @param resourceXml
	 * @param resource
	 * @throws InvalidFeatureNameException
	 * @throws InvalidFeatureTypeException
	 * @throws InvalidFeatureValueException
	 * @throws InvalidResourceTypeException
	 */
	private void setResourceFeatures(resource resourceXml, Resource resource) throws InvalidFeatureNameException, InvalidFeatureTypeException, InvalidFeatureValueException, InvalidResourceTypeException
	{
		if(resourceXml.type.compareTo(resource.getType().getResourceTypeName()) != 0)
		{
			throw new InvalidResourceTypeException();
		}
		
		for(int i=0; i<resourceXml.features.size(); i++)
		{
			//resource.SetFeatureValue(featureName, value)
			String key = resourceXml.features.get(i).getName();
			String value = resourceXml.features.get(i).getValue();
			//try {
				FeatureType ftype = resource.getFeatureType(key);
				
				switch(ftype)
				{
				case INT:
					Integer valuei = Integer.parseInt(value);
					resource.setFeatureValue(key, valuei);
					break;
				case FLOAT:
					Float valuef = Float.parseFloat(value);
					resource.setFeatureValue(key, valuef);
					break;
				case ENUM:
					//Class cl = Class.forName(resourceType.GetEnumClassName(key));
					//Enum<?> valuee = Enum.valueOf(cl, value);
					//resource.SetFeatureValue(key, valuee);
					String valuee = new String(value);
					resource.setFeatureValue(key, valuee);
					break;
				}
				
				try {
					resource.setQuantity(Integer.parseInt( resourceXml.quantity) );
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvalidValueException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				
			//} catch (InvalidFeatureNameException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
			//} catch (InvalidFeatureTypeException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
			//} catch (InvalidFeatureValueException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
			//}
		}
	}

}
